﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using NDatabase.Api;
using NDatabase.Api.Query;

namespace NDatabase.Repository
{
    /// <summary>
    /// Entity
    /// </summary>
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
    public class NDBSet<TEntity> : INDBSet<TEntity> where TEntity : Entity
    {
        /// <summary>
        /// Gets the context.
        /// </summary>
        /// <value>
        /// The context.
        /// </value>
        public INDatabaseContext Context { get; private set; }

        public NDBSet()
        {

        }

        /// <summary>
        /// Registers the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Register(INDatabaseContext context)
        {
            Context = context;
        }

        /// <summary>
        /// Ases the queryable.
        /// </summary>
        /// <returns></returns>
        public IQueryable<TEntity> AsQueryable()
        {
            return Context.DataBase.AsQueryable<TEntity>();
        }

        /// <summary>
        /// Finds the one.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <returns></returns>
        public TEntity FindOne(string id)
        {
            return Context.DataBase.AsQueryable<TEntity>().FirstOrDefault(x => x.Id == id);
        }

        /// <summary>
        /// Saves the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Save(TEntity entity)
        {
            if (string.IsNullOrEmpty(entity.Id))
            {
                entity.Id = CreateId();
            }
            else
            {
                var model = FindOne(entity.Id);
                if (model == null)
                {
                    entity.Id = CreateId();
                }
                else
                {
                    Remove(entity.Id);
                }
            }
            Context.DataBase.Store(entity);
        }

        /// <summary>
        /// Removes the specified identifier.
        /// </summary>
        /// <param name="id">The identifier.</param>
        public void Remove(string id)
        {
            var model = FindOne(id);
            Context.DataBase.Delete(model);
        }

        /// <summary>
        /// Removes the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Remove(TEntity entity)
        {
            Remove(entity.Id);
        }

        /// <summary>
        /// Counts this instance.
        /// </summary>
        /// <returns></returns>
        public int Count()
        {
           return (int)Context.DataBase.Query<TEntity>().Count();
        }

        /// <summary>
        /// Counts the specified condition.
        /// </summary>
        /// <param name="condition">The condition.</param>
        /// <returns></returns>
        public int Count(Expression<Func<TEntity, bool>> condition)
        {
            return AsQueryable().Count(condition);
        }

        /// <summary>
        /// Creates the identifier.
        /// </summary>
        /// <returns></returns>
        private static string CreateId()
        {
            var i = Guid.NewGuid().ToByteArray().Aggregate<byte, long>(1, (current, b) => current*((int) b + 1));
            return string.Format("{0:x}", i - DateTime.Now.Ticks); 
        }
    }
}
